//package org.cybzacg.string;
//
//import org.apache.logging.log4j.core.util.ArrayUtils;
//
//import java.io.UnsupportedEncodingException;
//import java.nio.CharBuffer;
//import java.nio.charset.Charset;
//import java.text.Normalizer;
//import java.util.*;
//import java.util.function.Supplier;
//import java.util.regex.Pattern;
//import java.util.stream.Collectors;
//
///**
// 对 {@link String} 的操作，且是 {@code null} 安全的。
// <ul>
// <li><strong>IsEmpty/IsBlank</strong>
// 检查字符串是否包含文本</li>
// <li><strong>Trim/Strip</strong>
// 移除前导和尾随空白字符</li>
// <li><strong>Equals/Compare</strong>
// 以 null 安全的方式比较两个字符串</li>
// <li><strong>startsWith</strong>
// 以 null 安全的方式检查字符串是否以某个前缀开头</li>
// <li><strong>endsWith</strong>
// 以 null 安全的方式检查字符串是否以某个后缀结尾</li>
// <li><strong>IndexOf/LastIndexOf/Contains</strong>
// null 安全的索引检查
// <li><strong>IndexOfAny/LastIndexOfAny/IndexOfAnyBut/LastIndexOfAnyBut</strong>
// 一组字符串中任意一个的索引位置</li>
// <li><strong>ContainsOnly/ContainsNone/ContainsAny</strong>
// 检查字符串是否仅包含 / 不包含 / 包含任意这些字符</li>
// <li><strong>Substring/Left/Right/Mid</strong>
// null 安全的子字符串提取</li>
// <li><strong>SubstringBefore/SubstringAfter/SubstringBetween</strong>
// 相对于其他字符串的子字符串提取</li>
// <li><strong>Split/Join</strong>
// 将字符串拆分为子字符串数组，反之亦然</li>
// <li><strong>Remove/Delete</strong>
// 移除字符串的部分内容</li>
// <li><strong>Replace/Overlay</strong>
// 搜索字符串并将一个字符串替换为另一个字符串</li>
// <li><strong>Chomp/Chop</strong>
// 移除字符串的最后部分</li>
// <li><strong>AppendIfMissing</strong>
// 如果后缀不存在，则在字符串末尾添加后缀</li>
// <li><strong>PrependIfMissing</strong>
// 如果前缀不存在，则在字符串开头添加前缀</li>
// <li><strong>LeftPad/RightPad/Center/Repeat</strong>
// 为字符串填充内容</li>
// <li><strong>UpperCase/LowerCase/SwapCase/Capitalize/Uncapitalize</strong>
// 改变字符串的大小写</li>
// <li><strong>CountMatches</strong>
// 计算一个字符串在另一个字符串中出现的次数</li>
// <li><strong>IsAlpha/IsNumeric/IsWhitespace/IsAsciiPrintable</strong>
// 检查字符串中的字符</li>
// <li><strong>DefaultString</strong>
// 防止输入字符串为 null</li>
// <li><strong>Rotate</strong>
// 旋转（循环移位）字符串</li>
// <li><strong>Reverse/ReverseDelimited</strong>
// 反转字符串</li>
// <li><strong>Abbreviate</strong>
// 使用省略号或其他指定字符串缩写字符串</li>
// <li><strong>Difference</strong>
// 比较字符串并报告它们的差异</li>
// <li><strong>LevenshteinDistance</strong>
// 将一个字符串转换为另一个字符串所需的修改次数</li>
// </ul>
// <p>{@link StringUtils}类定义了与字符串处理相关的某些术语。</p>
// <ul>
// <li>null - {@code null}</li>
// <li>empty - 长度为零的字符串（{@code ""}）</li>
// <li>space - 空格字符（{@code ' '}，字符32）</li>
// <li>whitespace - 由{@link Character#isWhitespace(char)}定义的字符</li>
// <li>trim - 如{@link String#trim()}中定义的小于等于32的字符</li>
// </ul>
// <p>{@link StringUtils}安静地处理{@code null}输入字符串。
// 也就是说，{@code null} 输入将返回 {@code null}。
// 当返回 {@code boolean} 或 {@code int} 时，具体细节因方法而异。</p>
// <p>这种{@code null}处理的一个副作用是，{@link NullPointerException}应被视为{@link StringUtils}中的一个bug。</p>
// <p>此类中的方法在其Javadoc注释中包含示例代码以解释其操作。
// 符号 {@code *} 用于表示任何输入，包括 {@code null}。</p>
// <p>#ThreadSafe#</p>
// @see String
// @since 1.0
// */
//public class StringUtils {
//
//    /**
//     * 这是一个三个字符长度的省略号。Unicode 中有一个水平省略号字符 U+2026 …，但这并不是它。
//     */
//    private static final String ELLIPSIS3 = "...";
//
//    /**
//     * 表示空格字符的字符串。
//     *
//     * @since 3.2
//     */
//    public static final String SPACE = " ";
//
//    /**
//     * 空字符串 {@code ""}。
//     * @since 2.0
//     */
//    public static final String EMPTY = "";
//
//    /**
//     * null 字符串 {@code null}。仅包内可见。
//     */
//    static final String NULL = null;
//
//    /**
//     * 换行符 LF ("\n") 的字符串表示。
//     *   字符和字符串字面量的转义序列
//     * @since 3.2
//     */
//    public static final String LF = "\n";
//
//    /**
//     * 回车符 CR ("\r") 的字符串表示。
//     * 字符和字符串字面量的转义序列
//     * @since 3.2
//     */
//    public static final String CR = "\r";
//
//    /**
//     * 表示未找到索引的情况。
//     * @since 2.1
//     */
//    public static final int INDEX_NOT_FOUND = -1;
//
//    /**
//     * 填充常量可以扩展的最大大小。
//     */
//    private static final int PAD_LIMIT = 8192;
//
//    /**
//     * 默认递归替换的最大深度，直到无法继续进行搜索替换为止。
//     */
//    private static final int DEFAULT_TTL = 5;
//
//    /**
//     * 在 {@link #stripAccents(String)} 方法中使用的正则表达式模式。
//     */
//    private static final Pattern STRIP_ACCENTS_PATTERN = Pattern.compile("\\p{InCombiningDiacriticalMarks}+"); //$NON-NLS-1$
//
//
//    /**
//     * 截取字符串，返回指定长度的字符串。
//     * @param str 要截取的字符串
//     * @param maxWidth 最大长度
//     * @return 返回截取部分的字符串
//     */
//    public static String abbreviate(final String str, final int maxWidth) {
//        return abbreviate(str, ELLIPSIS3, 0, maxWidth);
//    }
//
//    /**
//     * 截取字符串，返回指定偏移量和最大长度的字符串。
//     * @param str 要截取的源字符串
//     * @param offset 截取的偏移量
//     * @param maxWidth 最大长度
//     * @return 截取后的字符串
//     */
//    public static String abbreviate(final String str, final int offset, final int maxWidth) {
//        return abbreviate(str, ELLIPSIS3, offset, maxWidth);
//    }
//
//    /**
//     * 对指定字符串进行缩写处理，当原字符串长度超过最大宽度时，使用指定的缩写标记（如"..."）进行截断缩写。
//     * @param str 待处理的源字符串
//     * @param abbrevMarker 缩写标记
//     * @param maxWidth 最大长度
//     * @return 截取后的字符串
//     */
//    public static String abbreviate(final String str, final String abbrevMarker, final int maxWidth) {
//        return abbreviate(str, abbrevMarker, 0, maxWidth);
//    }
//
//    /**
//     * 对指定字符串进行缩写处理，当原字符串长度超过最大宽度时，使用指定的缩写标记（如"..."）进行截断缩写。
//     * 支持通过偏移量（offset）指定缩写的起始位置，最终缩写后的字符串长度不超过maxWidth（包含缩写标记长度）。
//     *
//     * @param str         待缩写的原始字符串，若为null或空字符串则直接返回
//     * @param abbrevMarker 用于表示缩写的标记（如"..."），若为null或空字符串且需缩写时，直接截断至maxWidth长度
//     * @param offset      缩写的起始偏移量，用于指定从字符串的哪个位置开始处理缩写，超出字符串长度时自动调整为字符串长度
//     * @param maxWidth    缩写后字符串的最大允许长度（包含abbrevMarker的长度）
//     * @return 缩写后的字符串；若原字符串长度不超过maxWidth，则返回原字符串
//     * @throws IllegalArgumentException 当maxWidth小于最小缩写宽度要求时抛出
//     *                                  （无偏移时最小为abbrevMarker长度+1；有偏移时最小为2*abbrevMarker长度+1）
//     */
//    public static String abbreviate(final String str, final String abbrevMarker, int offset, final int maxWidth) {
//        // 特殊情况：原字符串非空、缩写标记为空且maxWidth有效时，直接截断至maxWidth长度
//        if (isNotEmpty(str) && EMPTY.equals(abbrevMarker) && maxWidth > 0) {
//            return substring(str, 0, maxWidth);
//        }
//        // 若原字符串或缩写标记为空，无法进行有效缩写，返回原字符串
//        if (isAnyEmpty(str, abbrevMarker)) {
//            return str;
//        }
//
//        // 缩写标记的长度
//        final int abbrevMarkerLength = abbrevMarker.length();
//        // 无偏移时的最小缩写宽度：至少需要缩写标记+1个原字符（否则标记本身可能超过maxWidth）
//        final int minAbbrevWidth = abbrevMarkerLength + 1;
//        // 有偏移时的最小缩写宽度：需要两个标记长度+1（偏移前后各需部分字符+标记）
//        final int minAbbrevWidthOffset = abbrevMarkerLength + abbrevMarkerLength + 1;
//
//        // 校验maxWidth是否满足无偏移时的最小要求
//        if (maxWidth < minAbbrevWidth) {
//            throw new IllegalArgumentException(String.format("Minimum abbreviation width is %d", minAbbrevWidth));
//        }
//
//        final int strLen = str.length();
//        // 原字符串长度不超过maxWidth，无需缩写，直接返回
//        if (strLen <= maxWidth) {
//            return str;
//        }
//
//        // 调整offset：若偏移量超过字符串长度，设为字符串长度（从末尾开始处理）
//        if (offset > strLen) {
//            offset = strLen;
//        }
//
//        // 调整offset：确保从offset开始剩余的字符串长度足够容纳（maxWidth - 标记长度）的字符
//        // 避免缩写后标记+剩余字符总长度超过maxWidth
//        if (strLen - offset < maxWidth - abbrevMarkerLength) {
//            offset = strLen - (maxWidth - abbrevMarkerLength);
//        }
//
//        // 偏移量较小（小于等于标记长度+1）时，直接从字符串开头取部分字符+标记
//        if (offset <= abbrevMarkerLength + 1) {
//            return str.substring(0, maxWidth - abbrevMarkerLength) + abbrevMarker;
//        }
//
//        // 校验maxWidth是否满足有偏移时的最小要求
//        if (maxWidth < minAbbrevWidthOffset) {
//            throw new IllegalArgumentException(String.format("Minimum abbreviation width with offset is %d", minAbbrevWidthOffset));
//        }
//
//        // 若offset之后的剩余部分仍需进一步缩写（加上标记后超过maxWidth），则递归处理偏移后的子串
//        if (offset + maxWidth - abbrevMarkerLength < strLen) {
//            return abbrevMarker + abbreviate(str.substring(offset), abbrevMarker, maxWidth - abbrevMarkerLength);
//        }
//
//        // 若offset之后的剩余部分足够短，直接取末尾（maxWidth - 标记长度）个字符+标记
//        return abbrevMarker + str.substring(strLen - (maxWidth - abbrevMarkerLength));
//    }
//
//
//    public static String abbreviateMiddle(final String str, final String middle, final int length) {
//        if (isAnyEmpty(str, middle) || length >= str.length() || length < middle.length() + 2) {
//            return str;
//        }
//        final int targetSting = length - middle.length();
//        final int startOffset = targetSting / 2 + targetSting % 2;
//        final int endOffset = str.length() - targetSting / 2;
//        return str.substring(0, startOffset) + middle + str.substring(endOffset);
//    }
//
//    public static String capitalize(final String str) {
//        if (isEmpty(str)) {
//            return str;
//        }
//        final int firstCodepoint = str.codePointAt(0);
//        final int newCodePoint = Character.toTitleCase(firstCodepoint);
//        if (firstCodepoint == newCodePoint) {
//            // already capitalized
//            return str;
//        }
//        final int[] newCodePoints = str.codePoints().toArray();
//        newCodePoints[0] = newCodePoint; // copy the first code point
//        return new String(newCodePoints, 0, newCodePoints.length);
//    }
//
//    public static String center(final String str, final int size) {
//        return center(str, size, ' ');
//    }
//
//
//    public static String center(String str, final int size, final char padChar) {
//        if (str == null || size <= 0) {
//            return str;
//        }
//        final int strLen = str.length();
//        final int pads = size - strLen;
//        if (pads <= 0) {
//            return str;
//        }
//        str = leftPad(str, strLen + pads / 2, padChar);
//        return rightPad(str, size, padChar);
//    }
//
//    public static String center(String str, final int size, String padStr) {
//        if (str == null || size <= 0) {
//            return str;
//        }
//        if (isEmpty(padStr)) {
//            padStr = SPACE;
//        }
//        final int strLen = str.length();
//        final int pads = size - strLen;
//        if (pads <= 0) {
//            return str;
//        }
//        str = leftPad(str, strLen + pads / 2, padStr);
//        return rightPad(str, size, padStr);
//    }
//
//    public static String chomp(final String str) {
//        if (isEmpty(str)) {
//            return str;
//        }
//        if (str.length() == 1) {
//            final char ch = str.charAt(0);
//            if (ch == CharUtils.CR || ch == CharUtils.LF) {
//                return EMPTY;
//            }
//            return str;
//        }
//        int lastIdx = str.length() - 1;
//        final char last = str.charAt(lastIdx);
//        if (last == CharUtils.LF) {
//            if (str.charAt(lastIdx - 1) == CharUtils.CR) {
//                lastIdx--;
//            }
//        } else if (last != CharUtils.CR) {
//            lastIdx++;
//        }
//        return str.substring(0, lastIdx);
//    }
//
//
//    public static String chop(final String str) {
//        if (str == null) {
//            return null;
//        }
//        final int strLen = str.length();
//        if (strLen < 2) {
//            return EMPTY;
//        }
//        final int lastIdx = strLen - 1;
//        final String ret = str.substring(0, lastIdx);
//        final char last = str.charAt(lastIdx);
//        if (last == CharUtils.LF && ret.charAt(lastIdx - 1) == CharUtils.CR) {
//            return ret.substring(0, lastIdx - 1);
//        }
//        return ret;
//    }
//
//    public static int compare(final String str1, final String str2, final boolean nullIsLess) {
//        if (str1 == str2) {
//            return 0;
//        }
//        if (str1 == null) {
//            return nullIsLess ? -1 : 1;
//        }
//        if (str2 == null) {
//            return nullIsLess ? 1 : -1;
//        }
//        return str1.compareTo(str2);
//    }
//
//    public static int compareIgnoreCase(final String str1, final String str2, final boolean nullIsLess) {
//        if (str1 == str2) {
//            return 0;
//        }
//        if (str1 == null) {
//            return nullIsLess ? -1 : 1;
//        }
//        if (str2 == null) {
//            return nullIsLess ? 1 : -1;
//        }
//        return str1.compareToIgnoreCase(str2);
//    }
//
//    public static boolean contains(final CharSequence seq, final int searchChar) {
//        if (isEmpty(seq)) {
//            return false;
//        }
//        return CharSequenceUtils.indexOf(seq, searchChar, 0) >= 0;
//    }
//
//    public static boolean containsAny(final CharSequence cs, final char... searchChars) {
//        if (isEmpty(cs) || ArrayUtils.isEmpty(searchChars)) {
//            return false;
//        }
//        final int csLength = cs.length();
//        final int searchLength = searchChars.length;
//        final int csLast = csLength - 1;
//        final int searchLast = searchLength - 1;
//        for (int i = 0; i < csLength; i++) {
//            final char ch = cs.charAt(i);
//            for (int j = 0; j < searchLength; j++) {
//                if (searchChars[j] == ch) {
//                    if (!Character.isHighSurrogate(ch) || j == searchLast || i < csLast && searchChars[j + 1] == cs.charAt(i + 1)) {
//                        return true;
//                    }
//                }
//            }
//        }
//        return false;
//    }
//
//
//    public static boolean containsAny(final CharSequence cs, final CharSequence searchChars) {
//        if (searchChars == null) {
//            return false;
//        }
//        return containsAny(cs, CharSequenceUtils.toCharArray(searchChars));
//    }
//
//    public static boolean containsNone(final CharSequence cs, final char... searchChars) {
//        if (cs == null || searchChars == null) {
//            return true;
//        }
//        final int csLen = cs.length();
//        final int csLast = csLen - 1;
//        final int searchLen = searchChars.length;
//        final int searchLast = searchLen - 1;
//        for (int i = 0; i < csLen; i++) {
//            final char ch = cs.charAt(i);
//            for (int j = 0; j < searchLen; j++) {
//                if (searchChars[j] == ch) {
//                    if (!Character.isHighSurrogate(ch) || j == searchLast || i < csLast && searchChars[j + 1] == cs.charAt(i + 1)) {
//                        return false;
//                    }
//                }
//            }
//        }
//        return true;
//    }
//
//    public static boolean containsNone(final CharSequence cs, final String invalidChars) {
//        if (invalidChars == null) {
//            return true;
//        }
//        return containsNone(cs, invalidChars.toCharArray());
//    }
//
//    public static boolean containsOnly(final CharSequence cs, final char... valid) {
//        // All these pre-checks are to maintain API with an older version
//        if (valid == null || cs == null) {
//            return false;
//        }
//        if (cs.length() == 0) {
//            return true;
//        }
//        if (valid.length == 0) {
//            return false;
//        }
//        return indexOfAnyBut(cs, valid) == INDEX_NOT_FOUND;
//    }
//
//    public static boolean containsOnly(final CharSequence cs, final String validChars) {
//        if (cs == null || validChars == null) {
//            return false;
//        }
//        return containsOnly(cs, validChars.toCharArray());
//    }
//
//    public static boolean containsWhitespace(final CharSequence seq) {
//        if (isEmpty(seq)) {
//            return false;
//        }
//        final int strLen = seq.length();
//        for (int i = 0; i < strLen; i++) {
//            if (Character.isWhitespace(seq.charAt(i))) {
//                return true;
//            }
//        }
//        return false;
//    }
//
//    private static void convertRemainingAccentCharacters(final StringBuilder decomposed) {
//        for (int i = 0; i < decomposed.length(); i++) {
//            final char charAt = decomposed.charAt(i);
//            switch (charAt) {
//                case '\u0141' -> decomposed.setCharAt(i, 'L');
//                case '\u0142' -> decomposed.setCharAt(i, 'l');
//
//                // D with stroke
//                case '\u0110' ->
//                    // LATIN CAPITAL LETTER D WITH STROKE
//                        decomposed.setCharAt(i, 'D');
//                case '\u0111' ->
//                    // LATIN SMALL LETTER D WITH STROKE
//                        decomposed.setCharAt(i, 'd');
//
//                // I with bar
//                case '\u0197' -> decomposed.setCharAt(i, 'I');
//                case '\u0268', '\u1DA4' -> decomposed.setCharAt(i, 'i');
//                case '\u1D7B', '\u1DA7' -> decomposed.setCharAt(i, 'I');
//
//                // U with bar
//                case '\u0244' ->
//                    // LATIN CAPITAL LETTER U BAR
//                        decomposed.setCharAt(i, 'U');
//                case '\u0289' ->
//                    // LATIN SMALL LETTER U BAR
//                        decomposed.setCharAt(i, 'u');
//                case '\u1D7E' ->
//                    // LATIN SMALL CAPITAL LETTER U WITH STROKE
//                        decomposed.setCharAt(i, 'U');
//                case '\u1DB6' ->
//                    // MODIFIER LETTER SMALL U BAR
//                        decomposed.setCharAt(i, 'u');
//
//                // T with stroke
//                case '\u0166' ->
//                    // LATIN CAPITAL LETTER T WITH STROKE
//                        decomposed.setCharAt(i, 'T');
//                case '\u0167' ->
//                    // LATIN SMALL LETTER T WITH STROKE
//                        decomposed.setCharAt(i, 't');
//                default -> {
//                    break;
//                }
//            }
//        }
//    }
//
//    public static int countMatches(final CharSequence str, final char ch) {
//        if (isEmpty(str)) {
//            return 0;
//        }
//        int count = 0;
//        // We could also call str.toCharArray() for faster lookups but that would generate more garbage.
//        for (int i = 0; i < str.length(); i++) {
//            if (ch == str.charAt(i)) {
//                count++;
//            }
//        }
//        return count;
//    }
//
//    public static int countMatches(final CharSequence str, final CharSequence sub) {
//        if (isEmpty(str) || isEmpty(sub)) {
//            return 0;
//        }
//        int count = 0;
//        int idx = 0;
//        while ((idx = CharSequenceUtils.indexOf(str, sub, idx)) != INDEX_NOT_FOUND) {
//            count++;
//            idx += sub.length();
//        }
//        return count;
//    }
//
//    public static <T extends CharSequence> T defaultIfBlank(final T str, final T defaultStr) {
//        return isBlank(str) ? defaultStr : str;
//    }
//
//    public static <T extends CharSequence> T defaultIfEmpty(final T str, final T defaultStr) {
//        return isEmpty(str) ? defaultStr : str;
//    }
//
//    public static String defaultString(final String str) {
//        return Objects.toString(str, EMPTY);
//    }
//
//
//    public static String deleteWhitespace(final String str) {
//        if (isEmpty(str)) {
//            return str;
//        }
//        final int sz = str.length();
//        final char[] chs = new char[sz];
//        int count = 0;
//        for (int i = 0; i < sz; i++) {
//            if (!Character.isWhitespace(str.charAt(i))) {
//                chs[count++] = str.charAt(i);
//            }
//        }
//        if (count == sz) {
//            return str;
//        }
//        if (count == 0) {
//            return EMPTY;
//        }
//        return new String(chs, 0, count);
//    }
//
//    public static String difference(final String str1, final String str2) {
//        if (str1 == null) {
//            return str2;
//        }
//        if (str2 == null) {
//            return str1;
//        }
//        final int at = indexOfDifference(str1, str2);
//        if (at == INDEX_NOT_FOUND) {
//            return EMPTY;
//        }
//        return str2.substring(at);
//    }
//
//    public static byte[] getBytes(final String string, final Charset charset) {
//        return string == null ? ArrayUtils.EMPTY_BYTE_ARRAY : string.getBytes(Charsets.toCharset(charset));
//    }
//
//    public static byte[] getBytes(final String string, final String charset) throws UnsupportedEncodingException {
//        return string == null ? ArrayUtils.EMPTY_BYTE_ARRAY : string.getBytes(Charsets.toCharsetName(charset));
//    }
//
//    public static String getCommonPrefix(final String... strs) {
//        if (ArrayUtils.isEmpty(strs)) {
//            return EMPTY;
//        }
//        final int smallestIndexOfDiff = indexOfDifference(strs);
//        if (smallestIndexOfDiff == INDEX_NOT_FOUND) {
//            // all strings were identical
//            if (strs[0] == null) {
//                return EMPTY;
//            }
//            return strs[0];
//        }
//        if (smallestIndexOfDiff == 0) {
//            // there were no common initial characters
//            return EMPTY;
//        }
//        // we found a common initial character sequence
//        return strs[0].substring(0, smallestIndexOfDiff);
//    }
//
//    public static String getDigits(final String str) {
//        if (isEmpty(str)) {
//            return str;
//        }
//        final int sz = str.length();
//        final StringBuilder strDigits = new StringBuilder(sz);
//        for (int i = 0; i < sz; i++) {
//            final char tempChar = str.charAt(i);
//            if (Character.isDigit(tempChar)) {
//                strDigits.append(tempChar);
//            }
//        }
//        return strDigits.toString();
//    }
//
//    public static <T extends CharSequence> T getIfBlank(final T str, final Supplier<T> defaultSupplier) {
//        return isBlank(str) ? Suppliers.get(defaultSupplier) : str;
//    }
//
//    public static <T extends CharSequence> T getIfEmpty(final T str, final Supplier<T> defaultSupplier) {
//        return isEmpty(str) ? Suppliers.get(defaultSupplier) : str;
//    }
//
//
//    public static int indexOf(final CharSequence seq, final int searchChar) {
//        if (isEmpty(seq)) {
//            return INDEX_NOT_FOUND;
//        }
//        return CharSequenceUtils.indexOf(seq, searchChar, 0);
//    }
//
//    public static int indexOf(final CharSequence seq, final int searchChar, final int startPos) {
//        if (isEmpty(seq)) {
//            return INDEX_NOT_FOUND;
//        }
//        return CharSequenceUtils.indexOf(seq, searchChar, startPos);
//    }
//
//    public static int indexOfAny(final CharSequence cs, final char... searchChars) {
//        return indexOfAny(cs, 0, searchChars);
//    }
//
//    public static int indexOfAny(final CharSequence str, final CharSequence... searchStrs) {
//        if (str == null || searchStrs == null) {
//            return INDEX_NOT_FOUND;
//        }
//        // String's can't have a MAX_VALUEth index.
//        int ret = Integer.MAX_VALUE;
//        int tmp;
//        for (final CharSequence search : searchStrs) {
//            if (search == null) {
//                continue;
//            }
//            tmp = CharSequenceUtils.indexOf(str, search, 0);
//            if (tmp == INDEX_NOT_FOUND) {
//                continue;
//            }
//            if (tmp < ret) {
//                ret = tmp;
//            }
//        }
//        return ret == Integer.MAX_VALUE ? INDEX_NOT_FOUND : ret;
//    }
//
//    public static int indexOfAny(final CharSequence cs, final int csStart, final char... searchChars) {
//        if (isEmpty(cs) || ArrayUtils.isEmpty(searchChars)) {
//            return INDEX_NOT_FOUND;
//        }
//        final int csLen = cs.length();
//        final int csLast = csLen - 1;
//        final int searchLen = searchChars.length;
//        final int searchLast = searchLen - 1;
//        for (int i = csStart; i < csLen; i++) {
//            final char ch = cs.charAt(i);
//            for (int j = 0; j < searchLen; j++) {
//                if (searchChars[j] == ch) {
//                    // ch is a supplementary character
//                    if (i >= csLast || j >= searchLast || !Character.isHighSurrogate(ch) || searchChars[j + 1] == cs.charAt(i + 1)) {
//                        return i;
//                    }
//                }
//            }
//        }
//        return INDEX_NOT_FOUND;
//    }
//
//    public static int indexOfAny(final CharSequence cs, final String searchChars) {
//        if (isEmpty(cs) || isEmpty(searchChars)) {
//            return INDEX_NOT_FOUND;
//        }
//        return indexOfAny(cs, searchChars.toCharArray());
//    }
//
//    public static int indexOfAnyBut(final CharSequence cs, final char... searchChars) {
//        if (isEmpty(cs) || ArrayUtils.isEmpty(searchChars)) {
//            return INDEX_NOT_FOUND;
//        }
//        return indexOfAnyBut(cs, CharBuffer.wrap(searchChars));
//    }
//
//    public static int indexOfAnyBut(final CharSequence seq, final CharSequence searchChars) {
//        if (isEmpty(seq) || isEmpty(searchChars)) {
//            return INDEX_NOT_FOUND;
//        }
//        final Set<Integer> searchSetCodePoints = searchChars.codePoints()
//                .boxed().collect(Collectors.toSet());
//        // advance character index from one interpreted codepoint to the next
//        for (int curSeqCharIdx = 0; curSeqCharIdx < seq.length();) {
//            final int curSeqCodePoint = Character.codePointAt(seq, curSeqCharIdx);
//            if (!searchSetCodePoints.contains(curSeqCodePoint)) {
//                return curSeqCharIdx;
//            }
//            curSeqCharIdx += Character.charCount(curSeqCodePoint); // skip indices to paired low-surrogates
//        }
//        return INDEX_NOT_FOUND;
//    }
//
//    public static int indexOfDifference(final CharSequence... css) {
//        if (ArrayUtils.getLength(css) <= 1) {
//            return INDEX_NOT_FOUND;
//        }
//        boolean anyStringNull = false;
//        boolean allStringsNull = true;
//        final int arrayLen = css.length;
//        int shortestStrLen = Integer.MAX_VALUE;
//        int longestStrLen = 0;
//        // find the min and max string lengths; this avoids checking to make
//        // sure we are not exceeding the length of the string each time through
//        // the bottom loop.
//        for (final CharSequence cs : css) {
//            if (cs == null) {
//                anyStringNull = true;
//                shortestStrLen = 0;
//            } else {
//                allStringsNull = false;
//                shortestStrLen = Math.min(cs.length(), shortestStrLen);
//                longestStrLen = Math.max(cs.length(), longestStrLen);
//            }
//        }
//        // handle lists containing all nulls or all empty strings
//        if (allStringsNull || longestStrLen == 0 && !anyStringNull) {
//            return INDEX_NOT_FOUND;
//        }
//        if (shortestStrLen == 0) {
//            return 0;
//        }
//        // find the position with the first difference across all strings
//        int firstDiff = -1;
//        for (int stringPos = 0; stringPos < shortestStrLen; stringPos++) {
//            final char comparisonChar = css[0].charAt(stringPos);
//            for (int arrayPos = 1; arrayPos < arrayLen; arrayPos++) {
//                if (css[arrayPos].charAt(stringPos) != comparisonChar) {
//                    firstDiff = stringPos;
//                    break;
//                }
//            }
//            if (firstDiff != -1) {
//                break;
//            }
//        }
//        if (firstDiff == -1 && shortestStrLen != longestStrLen) {
//            return shortestStrLen;
//        }
//        return firstDiff;
//    }
//
//    public static int indexOfDifference(final CharSequence cs1, final CharSequence cs2) {
//        if (cs1 == cs2) {
//            return INDEX_NOT_FOUND;
//        }
//        if (cs1 == null || cs2 == null) {
//            return 0;
//        }
//        int i;
//        for (i = 0; i < cs1.length() && i < cs2.length(); ++i) {
//            if (cs1.charAt(i) != cs2.charAt(i)) {
//                break;
//            }
//        }
//        if (i < cs2.length() || i < cs1.length()) {
//            return i;
//        }
//        return INDEX_NOT_FOUND;
//    }
//
//    public static boolean isAllBlank(final CharSequence... css) {
//        if (ArrayUtils.isEmpty(css)) {
//            return true;
//        }
//        for (final CharSequence cs : css) {
//            if (isNotBlank(cs)) {
//                return false;
//            }
//        }
//        return true;
//    }
//
//    public static boolean isAllEmpty(final CharSequence... css) {
//        if (ArrayUtils.isEmpty(css)) {
//            return true;
//        }
//        for (final CharSequence cs : css) {
//            if (isNotEmpty(cs)) {
//                return false;
//            }
//        }
//        return true;
//    }
//
//    public static boolean isAllLowerCase(final CharSequence cs) {
//        if (isEmpty(cs)) {
//            return false;
//        }
//        final int sz = cs.length();
//        for (int i = 0; i < sz; i++) {
//            if (!Character.isLowerCase(cs.charAt(i))) {
//                return false;
//            }
//        }
//        return true;
//    }
//
//    public static boolean isAllUpperCase(final CharSequence cs) {
//        if (isEmpty(cs)) {
//            return false;
//        }
//        final int sz = cs.length();
//        for (int i = 0; i < sz; i++) {
//            if (!Character.isUpperCase(cs.charAt(i))) {
//                return false;
//            }
//        }
//        return true;
//    }
//
//    public static boolean isAlpha(final CharSequence cs) {
//        if (isEmpty(cs)) {
//            return false;
//        }
//        final int sz = cs.length();
//        for (int i = 0; i < sz; i++) {
//            if (!Character.isLetter(cs.charAt(i))) {
//                return false;
//            }
//        }
//        return true;
//    }
//
//    public static boolean isAlphanumeric(final CharSequence cs) {
//        if (isEmpty(cs)) {
//            return false;
//        }
//        final int sz = cs.length();
//        for (int i = 0; i < sz; i++) {
//            if (!Character.isLetterOrDigit(cs.charAt(i))) {
//                return false;
//            }
//        }
//        return true;
//    }
//
//    public static boolean isAlphanumericSpace(final CharSequence cs) {
//        if (cs == null) {
//            return false;
//        }
//        final int sz = cs.length();
//        for (int i = 0; i < sz; i++) {
//            final char nowChar = cs.charAt(i);
//            if (nowChar != ' ' && !Character.isLetterOrDigit(nowChar)) {
//                return false;
//            }
//        }
//        return true;
//    }
//
//    public static boolean isAlphaSpace(final CharSequence cs) {
//        if (cs == null) {
//            return false;
//        }
//        final int sz = cs.length();
//        for (int i = 0; i < sz; i++) {
//            final char nowChar = cs.charAt(i);
//            if (nowChar != ' ' && !Character.isLetter(nowChar)) {
//                return false;
//            }
//        }
//        return true;
//    }
//
//    public static boolean isAnyBlank(final CharSequence... css) {
//        if (ArrayUtils.isEmpty(css)) {
//            return false;
//        }
//        for (final CharSequence cs : css) {
//            if (isBlank(cs)) {
//                return true;
//            }
//        }
//        return false;
//    }
//
//
//    public static boolean isAnyEmpty(final CharSequence... css) {
//        if (ArrayUtils.isEmpty(css)) {
//            return false;
//        }
//        for (final CharSequence cs : css) {
//            if (isEmpty(cs)) {
//                return true;
//            }
//        }
//        return false;
//    }
//
//    public static boolean isAsciiPrintable(final CharSequence cs) {
//        if (cs == null) {
//            return false;
//        }
//        final int sz = cs.length();
//        for (int i = 0; i < sz; i++) {
//            if (!CharUtils.isAsciiPrintable(cs.charAt(i))) {
//                return false;
//            }
//        }
//        return true;
//    }
//
//    public static boolean isBlank(final CharSequence cs) {
//        final int strLen = length(cs);
//        if (strLen == 0) {
//            return true;
//        }
//        for (int i = 0; i < strLen; i++) {
//            if (!Character.isWhitespace(cs.charAt(i))) {
//                return false;
//            }
//        }
//        return true;
//    }
//
//    public static boolean isEmpty(final CharSequence cs) {
//        return cs == null || cs.length() == 0;
//    }
//
//    public static boolean isMixedCase(final CharSequence cs) {
//        if (isEmpty(cs) || cs.length() == 1) {
//            return false;
//        }
//        boolean containsUppercase = false;
//        boolean containsLowercase = false;
//        final int sz = cs.length();
//        for (int i = 0; i < sz; i++) {
//            final char nowChar = cs.charAt(i);
//            if (Character.isUpperCase(nowChar)) {
//                containsUppercase = true;
//            } else if (Character.isLowerCase(nowChar)) {
//                containsLowercase = true;
//            }
//            if (containsUppercase && containsLowercase) {
//                return true;
//            }
//        }
//        return false;
//    }
//
//    public static boolean isNoneBlank(final CharSequence... css) {
//        return !isAnyBlank(css);
//    }
//
//    public static boolean isNoneEmpty(final CharSequence... css) {
//        return !isAnyEmpty(css);
//    }
//
//    public static boolean isNotBlank(final CharSequence cs) {
//        return !isBlank(cs);
//    }
//    public static boolean isNotEmpty(final CharSequence cs) {
//        return !isEmpty(cs);
//    }
//
//    public static boolean isNumeric(final CharSequence cs) {
//        if (isEmpty(cs)) {
//            return false;
//        }
//        final int sz = cs.length();
//        for (int i = 0; i < sz; i++) {
//            if (!Character.isDigit(cs.charAt(i))) {
//                return false;
//            }
//        }
//        return true;
//    }
//
//    public static boolean isNumericSpace(final CharSequence cs) {
//        if (cs == null) {
//            return false;
//        }
//        final int sz = cs.length();
//        for (int i = 0; i < sz; i++) {
//            final char nowChar = cs.charAt(i);
//            if (nowChar != ' ' && !Character.isDigit(nowChar)) {
//                return false;
//            }
//        }
//        return true;
//    }
//
//
//    public static boolean isWhitespace(final CharSequence cs) {
//        if (cs == null) {
//            return false;
//        }
//        final int sz = cs.length();
//        for (int i = 0; i < sz; i++) {
//            if (!Character.isWhitespace(cs.charAt(i))) {
//                return false;
//            }
//        }
//        return true;
//    }
//
//
//    public static String join(final boolean[] array, final char delimiter) {
//        if (array == null) {
//            return null;
//        }
//        return join(array, delimiter, 0, array.length);
//    }
//
//    public static String join(final boolean[] array, final char delimiter, final int startIndex, final int endIndex) {
//        if (array == null) {
//            return null;
//        }
//        if (endIndex - startIndex <= 0) {
//            return EMPTY;
//        }
//        final StringBuilder stringBuilder = new StringBuilder(array.length * 5 + array.length - 1);
//        for (int i = startIndex; i < endIndex; i++) {
//            stringBuilder
//                    .append(array[i])
//                    .append(delimiter);
//        }
//        return stringBuilder.substring(0, stringBuilder.length() - 1);
//    }
//
//    public static String join(final byte[] array, final char delimiter) {
//        if (array == null) {
//            return null;
//        }
//        return join(array, delimiter, 0, array.length);
//    }
//
//    public static String join(final byte[] array, final char delimiter, final int startIndex, final int endIndex) {
//        if (array == null) {
//            return null;
//        }
//        if (endIndex - startIndex <= 0) {
//            return EMPTY;
//        }
//        final StringBuilder stringBuilder = new StringBuilder();
//        for (int i = startIndex; i < endIndex; i++) {
//            stringBuilder
//                    .append(array[i])
//                    .append(delimiter);
//        }
//        return stringBuilder.substring(0, stringBuilder.length() - 1);
//    }
//
//    public static String join(final char[] array, final char delimiter) {
//        if (array == null) {
//            return null;
//        }
//        return join(array, delimiter, 0, array.length);
//    }
//
//    public static String join(final char[] array, final char delimiter, final int startIndex, final int endIndex) {
//        if (array == null) {
//            return null;
//        }
//        if (endIndex - startIndex <= 0) {
//            return EMPTY;
//        }
//        final StringBuilder stringBuilder = new StringBuilder(array.length * 2 - 1);
//        for (int i = startIndex; i < endIndex; i++) {
//            stringBuilder
//                    .append(array[i])
//                    .append(delimiter);
//        }
//        return stringBuilder.substring(0, stringBuilder.length() - 1);
//    }
//
//    public static String join(final double[] array, final char delimiter) {
//        if (array == null) {
//            return null;
//        }
//        return join(array, delimiter, 0, array.length);
//    }
//
//    public static String join(final double[] array, final char delimiter, final int startIndex, final int endIndex) {
//        if (array == null) {
//            return null;
//        }
//        if (endIndex - startIndex <= 0) {
//            return EMPTY;
//        }
//        final StringBuilder stringBuilder = new StringBuilder();
//        for (int i = startIndex; i < endIndex; i++) {
//            stringBuilder
//                    .append(array[i])
//                    .append(delimiter);
//        }
//        return stringBuilder.substring(0, stringBuilder.length() - 1);
//    }
//
//    public static String join(final float[] array, final char delimiter) {
//        if (array == null) {
//            return null;
//        }
//        return join(array, delimiter, 0, array.length);
//    }
//
//    public static String join(final float[] array, final char delimiter, final int startIndex, final int endIndex) {
//        if (array == null) {
//            return null;
//        }
//        if (endIndex - startIndex <= 0) {
//            return EMPTY;
//        }
//        final StringBuilder stringBuilder = new StringBuilder();
//        for (int i = startIndex; i < endIndex; i++) {
//            stringBuilder
//                    .append(array[i])
//                    .append(delimiter);
//        }
//        return stringBuilder.substring(0, stringBuilder.length() - 1);
//    }
//
//    public static String join(final int[] array, final char separator) {
//        if (array == null) {
//            return null;
//        }
//        return join(array, separator, 0, array.length);
//    }
//
//    public static String join(final int[] array, final char delimiter, final int startIndex, final int endIndex) {
//        if (array == null) {
//            return null;
//        }
//        if (endIndex - startIndex <= 0) {
//            return EMPTY;
//        }
//        final StringBuilder stringBuilder = new StringBuilder();
//        for (int i = startIndex; i < endIndex; i++) {
//            stringBuilder
//                    .append(array[i])
//                    .append(delimiter);
//        }
//        return stringBuilder.substring(0, stringBuilder.length() - 1);
//    }
//
//    public static String join(final Iterable<?> iterable, final char separator) {
//        return iterable != null ? join(iterable.iterator(), separator) : null;
//    }
//
//    public static String join(final Iterable<?> iterable, final String separator) {
//        return iterable != null ? join(iterable.iterator(), separator) : null;
//    }
//
//    public static String join(final Iterator<?> iterator, final char separator) {
//        // handle null, zero and one elements before building a buffer
//        if (iterator == null) {
//            return null;
//        }
//        if (!iterator.hasNext()) {
//            return EMPTY;
//        }
//        return Streams.of(iterator).collect(LangCollectors.joining(ObjectUtils.toString(String.valueOf(separator)), EMPTY, EMPTY, ObjectUtils::toString));
//    }
//
//    public static String join(final Iterator<?> iterator, final String separator) {
//        // handle null, zero and one elements before building a buffer
//        if (iterator == null) {
//            return null;
//        }
//        if (!iterator.hasNext()) {
//            return EMPTY;
//        }
//        return Streams.of(iterator).collect(LangCollectors.joining(ObjectUtils.toString(separator), EMPTY, EMPTY, ObjectUtils::toString));
//    }
//
//    public static String join(final List<?> list, final char separator, final int startIndex, final int endIndex) {
//        if (list == null) {
//            return null;
//        }
//        final int noOfItems = endIndex - startIndex;
//        if (noOfItems <= 0) {
//            return EMPTY;
//        }
//        final List<?> subList = list.subList(startIndex, endIndex);
//        return join(subList.iterator(), separator);
//    }
//
//    public static String join(final List<?> list, final String separator, final int startIndex, final int endIndex) {
//        if (list == null) {
//            return null;
//        }
//        final int noOfItems = endIndex - startIndex;
//        if (noOfItems <= 0) {
//            return EMPTY;
//        }
//        final List<?> subList = list.subList(startIndex, endIndex);
//        return join(subList.iterator(), separator);
//    }
//
//    public static String join(final long[] array, final char separator) {
//        if (array == null) {
//            return null;
//        }
//        return join(array, separator, 0, array.length);
//    }
//
//    public static String join(final long[] array, final char delimiter, final int startIndex, final int endIndex) {
//        if (array == null) {
//            return null;
//        }
//        if (endIndex - startIndex <= 0) {
//            return EMPTY;
//        }
//        final StringBuilder stringBuilder = new StringBuilder();
//        for (int i = startIndex; i < endIndex; i++) {
//            stringBuilder
//                    .append(array[i])
//                    .append(delimiter);
//        }
//        return stringBuilder.substring(0, stringBuilder.length() - 1);
//    }
//
//    public static String join(final Object[] array, final char delimiter) {
//        if (array == null) {
//            return null;
//        }
//        return join(array, delimiter, 0, array.length);
//    }
//
//    public static String join(final Object[] array, final char delimiter, final int startIndex, final int endIndex) {
//        return join(array, String.valueOf(delimiter), startIndex, endIndex);
//    }
//
//    public static String join(final Object[] array, final String delimiter) {
//        return array != null ? join(array, ObjectUtils.toString(delimiter), 0, array.length) : null;
//    }
//
//    public static String join(final Object[] array, final String delimiter, final int startIndex, final int endIndex) {
//        return array != null ? Streams.of(array).skip(startIndex).limit(Math.max(0, endIndex - startIndex))
//                .collect(LangCollectors.joining(delimiter, EMPTY, EMPTY, ObjectUtils::toString)) : null;
//    }
//    public static String join(final short[] array, final char delimiter) {
//        if (array == null) {
//            return null;
//        }
//        return join(array, delimiter, 0, array.length);
//    }
//
//    public static String join(final short[] array, final char delimiter, final int startIndex, final int endIndex) {
//        if (array == null) {
//            return null;
//        }
//        if (endIndex - startIndex <= 0) {
//            return EMPTY;
//        }
//        final StringBuilder stringBuilder = new StringBuilder();
//        for (int i = startIndex; i < endIndex; i++) {
//            stringBuilder
//                    .append(array[i])
//                    .append(delimiter);
//        }
//        return stringBuilder.substring(0, stringBuilder.length() - 1);
//    }
//
//    @SafeVarargs
//    public static <T> String join(final T... elements) {
//        return join(elements, null);
//    }
//
//
//    public static String joinWith(final String delimiter, final Object... array) {
//        if (array == null) {
//            throw new IllegalArgumentException("Object varargs must not be null");
//        }
//        return join(array, delimiter);
//    }
//
//    public static int lastIndexOf(final CharSequence seq, final int searchChar) {
//        if (isEmpty(seq)) {
//            return INDEX_NOT_FOUND;
//        }
//        return CharSequenceUtils.lastIndexOf(seq, searchChar, seq.length());
//    }
//
//    public static int lastIndexOf(final CharSequence seq, final int searchChar, final int startPos) {
//        if (isEmpty(seq)) {
//            return INDEX_NOT_FOUND;
//        }
//        return CharSequenceUtils.lastIndexOf(seq, searchChar, startPos);
//    }
//
//    public static int lastIndexOfAny(final CharSequence str, final CharSequence... searchStrs) {
//        if (str == null || searchStrs == null) {
//            return INDEX_NOT_FOUND;
//        }
//        int ret = INDEX_NOT_FOUND;
//        int tmp;
//        for (final CharSequence search : searchStrs) {
//            if (search == null) {
//                continue;
//            }
//            tmp = CharSequenceUtils.lastIndexOf(str, search, str.length());
//            if (tmp > ret) {
//                ret = tmp;
//            }
//        }
//        return ret;
//    }
//
//
//    public static int lastOrdinalIndexOf(final CharSequence str, final CharSequence searchStr, final int ordinal) {
//        return ordinalIndexOf(str, searchStr, ordinal, true);
//    }
//
//    public static String left(final String str, final int len) {
//        if (str == null) {
//            return null;
//        }
//        if (len < 0) {
//            return EMPTY;
//        }
//        if (str.length() <= len) {
//            return str;
//        }
//        return str.substring(0, len);
//    }
//
//    public static String leftPad(final String str, final int size) {
//        return leftPad(str, size, ' ');
//    }
//
//
//    public static String leftPad(final String str, final int size, final char padChar) {
//        if (str == null) {
//            return null;
//        }
//        final int pads = size - str.length();
//        if (pads <= 0) {
//            return str; // returns original String when possible
//        }
//        if (pads > PAD_LIMIT) {
//            return leftPad(str, size, String.valueOf(padChar));
//        }
//        return repeat(padChar, pads).concat(str);
//    }
//
//    public static String leftPad(final String str, final int size, String padStr) {
//        if (str == null) {
//            return null;
//        }
//        if (isEmpty(padStr)) {
//            padStr = SPACE;
//        }
//        final int padLen = padStr.length();
//        final int strLen = str.length();
//        final int pads = size - strLen;
//        if (pads <= 0) {
//            return str; // returns original String when possible
//        }
//        if (padLen == 1 && pads <= PAD_LIMIT) {
//            return leftPad(str, size, padStr.charAt(0));
//        }
//        if (pads == padLen) {
//            return padStr.concat(str);
//        }
//        if (pads < padLen) {
//            return padStr.substring(0, pads).concat(str);
//        }
//        final char[] padding = new char[pads];
//        final char[] padChars = padStr.toCharArray();
//        for (int i = 0; i < pads; i++) {
//            padding[i] = padChars[i % padLen];
//        }
//        return new String(padding).concat(str);
//    }
//
//    public static int length(final CharSequence cs) {
//        return cs == null ? 0 : cs.length();
//    }
//
//
//    public static String lowerCase(final String str) {
//        if (str == null) {
//            return null;
//        }
//        return str.toLowerCase();
//    }
//
//    public static String lowerCase(final String str, final Locale locale) {
//        if (str == null) {
//            return null;
//        }
//        return str.toLowerCase(LocaleUtils.toLocale(locale));
//    }
//
//    private static int[] matches(final CharSequence first, final CharSequence second) {
//        final CharSequence max;
//        final CharSequence min;
//        if (first.length() > second.length()) {
//            max = first;
//            min = second;
//        } else {
//            max = second;
//            min = first;
//        }
//        final int range = Math.max(max.length() / 2 - 1, 0);
//        final int[] matchIndexes = ArrayFill.fill(new int[min.length()], -1);
//        final boolean[] matchFlags = new boolean[max.length()];
//        int matches = 0;
//        for (int mi = 0; mi < min.length(); mi++) {
//            final char c1 = min.charAt(mi);
//            for (int xi = Math.max(mi - range, 0), xn = Math.min(mi + range + 1, max.length()); xi < xn; xi++) {
//                if (!matchFlags[xi] && c1 == max.charAt(xi)) {
//                    matchIndexes[mi] = xi;
//                    matchFlags[xi] = true;
//                    matches++;
//                    break;
//                }
//            }
//        }
//        final char[] ms1 = new char[matches];
//        final char[] ms2 = new char[matches];
//        for (int i = 0, si = 0; i < min.length(); i++) {
//            if (matchIndexes[i] != -1) {
//                ms1[si] = min.charAt(i);
//                si++;
//            }
//        }
//        for (int i = 0, si = 0; i < max.length(); i++) {
//            if (matchFlags[i]) {
//                ms2[si] = max.charAt(i);
//                si++;
//            }
//        }
//        int transpositions = 0;
//        for (int mi = 0; mi < ms1.length; mi++) {
//            if (ms1[mi] != ms2[mi]) {
//                transpositions++;
//            }
//        }
//        int prefix = 0;
//        for (int mi = 0; mi < min.length(); mi++) {
//            if (first.charAt(mi) != second.charAt(mi)) {
//                break;
//            }
//            prefix++;
//        }
//        return new int[] { matches, transpositions / 2, prefix, max.length() };
//    }
//
//
//    public static String mid(final String str, int pos, final int len) {
//        if (str == null) {
//            return null;
//        }
//        if (len < 0 || pos > str.length()) {
//            return EMPTY;
//        }
//        if (pos < 0) {
//            pos = 0;
//        }
//        if (str.length() <= pos + len) {
//            return str.substring(pos);
//        }
//        return str.substring(pos, pos + len);
//    }
//
//    public static String normalizeSpace(final String str) {
//        // LANG-1020: Improved performance significantly by normalizing manually instead of using regex
//        // See https://github.com/librucha/commons-lang-normalizespaces-benchmark for performance test
//        if (isEmpty(str)) {
//            return str;
//        }
//        final int size = str.length();
//        final char[] newChars = new char[size];
//        int count = 0;
//        int whitespacesCount = 0;
//        boolean startWhitespaces = true;
//        for (int i = 0; i < size; i++) {
//            final char actualChar = str.charAt(i);
//            final boolean isWhitespace = Character.isWhitespace(actualChar);
//            if (isWhitespace) {
//                if (whitespacesCount == 0 && !startWhitespaces) {
//                    newChars[count++] = SPACE.charAt(0);
//                }
//                whitespacesCount++;
//            } else {
//                startWhitespaces = false;
//                newChars[count++] = actualChar == 160 ? 32 : actualChar;
//                whitespacesCount = 0;
//            }
//        }
//        if (startWhitespaces) {
//            return EMPTY;
//        }
//        return new String(newChars, 0, count - (whitespacesCount > 0 ? 1 : 0)).trim();
//    }
//
//    public static int ordinalIndexOf(final CharSequence str, final CharSequence searchStr, final int ordinal) {
//        return ordinalIndexOf(str, searchStr, ordinal, false);
//    }
//
//    private static int ordinalIndexOf(final CharSequence str, final CharSequence searchStr, final int ordinal, final boolean lastIndex) {
//        if (str == null || searchStr == null || ordinal <= 0) {
//            return INDEX_NOT_FOUND;
//        }
//        if (searchStr.length() == 0) {
//            return lastIndex ? str.length() : 0;
//        }
//        int found = 0;
//        // set the initial index beyond the end of the string
//        // this is to allow for the initial index decrement/increment
//        int index = lastIndex ? str.length() : INDEX_NOT_FOUND;
//        do {
//            if (lastIndex) {
//                index = CharSequenceUtils.lastIndexOf(str, searchStr, index - 1); // step backwards through string
//            } else {
//                index = CharSequenceUtils.indexOf(str, searchStr, index + 1); // step forwards through string
//            }
//            if (index < 0) {
//                return index;
//            }
//            found++;
//        } while (found < ordinal);
//        return index;
//    }
//
//    public static String overlay(final String str, String overlay, int start, int end) {
//        if (str == null) {
//            return null;
//        }
//        if (overlay == null) {
//            overlay = EMPTY;
//        }
//        final int len = str.length();
//        if (start < 0) {
//            start = 0;
//        }
//        if (start > len) {
//            start = len;
//        }
//        if (end < 0) {
//            end = 0;
//        }
//        if (end > len) {
//            end = len;
//        }
//        if (start > end) {
//            final int temp = start;
//            start = end;
//            end = temp;
//        }
//        return str.substring(0, start) + overlay + str.substring(end);
//    }
//
//    public static String remove(final String str, final char remove) {
//        if (isEmpty(str) || str.indexOf(remove) == INDEX_NOT_FOUND) {
//            return str;
//        }
//        final char[] chars = str.toCharArray();
//        int pos = 0;
//        for (int i = 0; i < chars.length; i++) {
//            if (chars[i] != remove) {
//                chars[pos++] = chars[i];
//            }
//        }
//        return new String(chars, 0, pos);
//    }
//
//    public static String removeStart(final String str, final char remove) {
//        if (isEmpty(str)) {
//            return str;
//        }
//        return str.charAt(0) == remove ? str.substring(1) : str;
//    }
//
//    public static String repeat(final char repeat, final int count) {
//        if (count <= 0) {
//            return EMPTY;
//        }
//        return new String(ArrayFill.fill(new char[count], repeat));
//    }
//
//    public static String repeat(final String repeat, final int count) {
//        // Performance tuned for 2.0 (JDK1.4)
//        if (repeat == null) {
//            return null;
//        }
//        if (count <= 0) {
//            return EMPTY;
//        }
//        final int inputLength = repeat.length();
//        if (count == 1 || inputLength == 0) {
//            return repeat;
//        }
//        if (inputLength == 1 && count <= PAD_LIMIT) {
//            return repeat(repeat.charAt(0), count);
//        }
//        final int outputLength = inputLength * count;
//        switch (inputLength) {
//            case 1:
//                return repeat(repeat.charAt(0), count);
//            case 2:
//                final char ch0 = repeat.charAt(0);
//                final char ch1 = repeat.charAt(1);
//                final char[] output2 = new char[outputLength];
//                for (int i = count * 2 - 2; i >= 0; i--, i--) {
//                    output2[i] = ch0;
//                    output2[i + 1] = ch1;
//                }
//                return new String(output2);
//            default:
//                final StringBuilder buf = new StringBuilder(outputLength);
//                for (int i = 0; i < count; i++) {
//                    buf.append(repeat);
//                }
//                return buf.toString();
//        }
//    }
//
//    public static String repeat(final String repeat, final String separator, final int count) {
//        if (repeat == null || separator == null) {
//            return repeat(repeat, count);
//        }
//        // given that repeat(String, int) is quite optimized, better to rely on it than try and splice this into it
//        final String result = repeat(repeat + separator, count);
//        return Strings.CS.removeEnd(result, separator);
//    }
//
//    public static String replaceChars(final String str, final char searchChar, final char replaceChar) {
//        if (str == null) {
//            return null;
//        }
//        return str.replace(searchChar, replaceChar);
//    }
//
//    public static String replaceChars(final String str, final String searchChars, String replaceChars) {
//        if (isEmpty(str) || isEmpty(searchChars)) {
//            return str;
//        }
//        replaceChars = ObjectUtils.toString(replaceChars);
//        boolean modified = false;
//        final int replaceCharsLength = replaceChars.length();
//        final int strLength = str.length();
//        final StringBuilder buf = new StringBuilder(strLength);
//        for (int i = 0; i < strLength; i++) {
//            final char ch = str.charAt(i);
//            final int index = searchChars.indexOf(ch);
//            if (index >= 0) {
//                modified = true;
//                if (index < replaceCharsLength) {
//                    buf.append(replaceChars.charAt(index));
//                }
//            } else {
//                buf.append(ch);
//            }
//        }
//        if (modified) {
//            return buf.toString();
//        }
//        return str;
//    }
//
//    public static String replaceEach(final String text, final String[] searchList, final String[] replacementList) {
//        return replaceEach(text, searchList, replacementList, false, 0);
//    }
//
//    private static String replaceEach(
//            final String text, final String[] searchList, final String[] replacementList, final boolean repeat, final int timeToLive) {
//
//        // mchyzer Performance note: This creates very few new objects (one major goal)
//        // let me know if there are performance requests, we can create a harness to measure
//        if (isEmpty(text) || ArrayUtils.isEmpty(searchList) || ArrayUtils.isEmpty(replacementList)) {
//            return text;
//        }
//
//        // if recursing, this shouldn't be less than 0
//        if (timeToLive < 0) {
//            throw new IllegalStateException("Aborting to protect against StackOverflowError - " +
//                    "output of one loop is the input of another");
//        }
//
//        final int searchLength = searchList.length;
//        final int replacementLength = replacementList.length;
//
//        // make sure lengths are ok, these need to be equal
//        if (searchLength != replacementLength) {
//            throw new IllegalArgumentException("Search and Replace array lengths don't match: "
//                    + searchLength
//                    + " vs "
//                    + replacementLength);
//        }
//
//        // keep track of which still have matches
//        final boolean[] noMoreMatchesForReplIndex = new boolean[searchLength];
//
//        // index on index that the match was found
//        int textIndex = -1;
//        int replaceIndex = -1;
//        int tempIndex;
//
//        // index of replace array that will replace the search string found
//        // NOTE: logic duplicated below START
//        for (int i = 0; i < searchLength; i++) {
//            if (noMoreMatchesForReplIndex[i] || isEmpty(searchList[i]) || replacementList[i] == null) {
//                continue;
//            }
//            tempIndex = text.indexOf(searchList[i]);
//
//            // see if we need to keep searching for this
//            if (tempIndex == -1) {
//                noMoreMatchesForReplIndex[i] = true;
//            } else if (textIndex == -1 || tempIndex < textIndex) {
//                textIndex = tempIndex;
//                replaceIndex = i;
//            }
//        }
//        // NOTE: logic mostly below END
//
//        // no search strings found, we are done
//        if (textIndex == -1) {
//            return text;
//        }
//
//        int start = 0;
//
//        // get a good guess on the size of the result buffer so it doesn't have to double if it goes over a bit
//        int increase = 0;
//
//        // count the replacement text elements that are larger than their corresponding text being replaced
//        for (int i = 0; i < searchList.length; i++) {
//            if (searchList[i] == null || replacementList[i] == null) {
//                continue;
//            }
//            final int greater = replacementList[i].length() - searchList[i].length();
//            if (greater > 0) {
//                increase += 3 * greater; // assume 3 matches
//            }
//        }
//        // have upper-bound at 20% increase, then let Java take over
//        increase = Math.min(increase, text.length() / 5);
//
//        final StringBuilder buf = new StringBuilder(text.length() + increase);
//
//        while (textIndex != -1) {
//
//            for (int i = start; i < textIndex; i++) {
//                buf.append(text.charAt(i));
//            }
//            buf.append(replacementList[replaceIndex]);
//
//            start = textIndex + searchList[replaceIndex].length();
//
//            textIndex = -1;
//            replaceIndex = -1;
//            // find the next earliest match
//            // NOTE: logic mostly duplicated above START
//            for (int i = 0; i < searchLength; i++) {
//                if (noMoreMatchesForReplIndex[i] || isEmpty(searchList[i]) || replacementList[i] == null) {
//                    continue;
//                }
//                tempIndex = text.indexOf(searchList[i], start);
//
//                // see if we need to keep searching for this
//                if (tempIndex == -1) {
//                    noMoreMatchesForReplIndex[i] = true;
//                } else if (textIndex == -1 || tempIndex < textIndex) {
//                    textIndex = tempIndex;
//                    replaceIndex = i;
//                }
//            }
//            // NOTE: logic duplicated above END
//
//        }
//        final int textLength = text.length();
//        for (int i = start; i < textLength; i++) {
//            buf.append(text.charAt(i));
//        }
//        final String result = buf.toString();
//        if (!repeat) {
//            return result;
//        }
//
//        return replaceEach(result, searchList, replacementList, repeat, timeToLive - 1);
//    }
//
//    public static String replaceEachRepeatedly(final String text, final String[] searchList, final String[] replacementList) {
//        final int timeToLive = Math.max(ArrayUtils.getLength(searchList), DEFAULT_TTL);
//        return replaceEach(text, searchList, replacementList, true, timeToLive);
//    }
//
//    public static String reverse(final String str) {
//        if (str == null) {
//            return null;
//        }
//        return new StringBuilder(str).reverse().toString();
//    }
//
//    public static String reverseDelimited(final String str, final char separatorChar) {
//        final String[] strs = split(str, separatorChar);
//        ArrayUtils.reverse(strs);
//        return join(strs, separatorChar);
//    }
//
//    public static String right(final String str, final int len) {
//        if (str == null) {
//            return null;
//        }
//        if (len < 0) {
//            return EMPTY;
//        }
//        if (str.length() <= len) {
//            return str;
//        }
//        return str.substring(str.length() - len);
//    }
//
//
//    public static String rightPad(final String str, final int size) {
//        return rightPad(str, size, ' ');
//    }
//
//
//    public static String rightPad(final String str, final int size, final char padChar) {
//        if (str == null) {
//            return null;
//        }
//        final int pads = size - str.length();
//        if (pads <= 0) {
//            return str; // returns original String when possible
//        }
//        if (pads > PAD_LIMIT) {
//            return rightPad(str, size, String.valueOf(padChar));
//        }
//        return str.concat(repeat(padChar, pads));
//    }
//
//
//    public static String rightPad(final String str, final int size, String padStr) {
//        if (str == null) {
//            return null;
//        }
//        if (isEmpty(padStr)) {
//            padStr = SPACE;
//        }
//        final int padLen = padStr.length();
//        final int strLen = str.length();
//        final int pads = size - strLen;
//        if (pads <= 0) {
//            return str; // returns original String when possible
//        }
//        if (padLen == 1 && pads <= PAD_LIMIT) {
//            return rightPad(str, size, padStr.charAt(0));
//        }
//        if (pads == padLen) {
//            return str.concat(padStr);
//        }
//        if (pads < padLen) {
//            return str.concat(padStr.substring(0, pads));
//        }
//        final char[] padding = new char[pads];
//        final char[] padChars = padStr.toCharArray();
//        for (int i = 0; i < pads; i++) {
//            padding[i] = padChars[i % padLen];
//        }
//        return str.concat(new String(padding));
//    }
//
//    public static String rotate(final String str, final int shift) {
//        if (str == null) {
//            return null;
//        }
//        final int strLen = str.length();
//        if (shift == 0 || strLen == 0 || shift % strLen == 0) {
//            return str;
//        }
//        final StringBuilder builder = new StringBuilder(strLen);
//        final int offset = -(shift % strLen);
//        builder.append(substring(str, offset));
//        builder.append(substring(str, 0, offset));
//        return builder.toString();
//    }
//
//    public static String[] split(final String str) {
//        return split(str, null, -1);
//    }
//
//
//    public static String[] split(final String str, final char separatorChar) {
//        return splitWorker(str, separatorChar, false);
//    }
//
//    public static String[] split(final String str, final String separatorChars) {
//        return splitWorker(str, separatorChars, -1, false);
//    }
//
//    public static String[] split(final String str, final String separatorChars, final int max) {
//        return splitWorker(str, separatorChars, max, false);
//    }
//
//    public static String[] splitByCharacterType(final String str) {
//        return splitByCharacterType(str, false);
//    }
//
//
//    private static String[] splitByCharacterType(final String str, final boolean camelCase) {
//        if (str == null) {
//            return null;
//        }
//        if (str.isEmpty()) {
//            return ArrayUtils.EMPTY_STRING_ARRAY;
//        }
//        final char[] c = str.toCharArray();
//        final List<String> list = new ArrayList<>();
//        int tokenStart = 0;
//        int currentType = Character.getType(c[tokenStart]);
//        for (int pos = tokenStart + 1; pos < c.length; pos++) {
//            final int type = Character.getType(c[pos]);
//            if (type == currentType) {
//                continue;
//            }
//            if (camelCase && type == Character.LOWERCASE_LETTER && currentType == Character.UPPERCASE_LETTER) {
//                final int newTokenStart = pos - 1;
//                if (newTokenStart != tokenStart) {
//                    list.add(new String(c, tokenStart, newTokenStart - tokenStart));
//                    tokenStart = newTokenStart;
//                }
//            } else {
//                list.add(new String(c, tokenStart, pos - tokenStart));
//                tokenStart = pos;
//            }
//            currentType = type;
//        }
//        list.add(new String(c, tokenStart, c.length - tokenStart));
//        return list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
//    }
//
//    public static String[] splitByCharacterTypeCamelCase(final String str) {
//        return splitByCharacterType(str, true);
//    }
//
//    public static String[] splitByWholeSeparator(final String str, final String separator) {
//        return splitByWholeSeparatorWorker(str, separator, -1, false);
//    }
//
//    public static String[] splitByWholeSeparator(final String str, final String separator, final int max) {
//        return splitByWholeSeparatorWorker(str, separator, max, false);
//    }
//
//    public static String[] splitByWholeSeparatorPreserveAllTokens(final String str, final String separator) {
//        return splitByWholeSeparatorWorker(str, separator, -1, true);
//    }
//
//    public static String[] splitByWholeSeparatorPreserveAllTokens(final String str, final String separator, final int max) {
//        return splitByWholeSeparatorWorker(str, separator, max, true);
//    }
//    private static String[] splitByWholeSeparatorWorker(final String str, final String separator, final int max, final boolean preserveAllTokens) {
//        if (str == null) {
//            return null;
//        }
//        final int len = str.length();
//        if (len == 0) {
//            return ArrayUtils.EMPTY_STRING_ARRAY;
//        }
//        if (separator == null || EMPTY.equals(separator)) {
//            // Split on whitespace.
//            return splitWorker(str, null, max, preserveAllTokens);
//        }
//        final int separatorLength = separator.length();
//        final ArrayList<String> substrings = new ArrayList<>();
//        int numberOfSubstrings = 0;
//        int beg = 0;
//        int end = 0;
//        while (end < len) {
//            end = str.indexOf(separator, beg);
//            if (end > -1) {
//                if (end > beg) {
//                    numberOfSubstrings += 1;
//                    if (numberOfSubstrings == max) {
//                        end = len;
//                        substrings.add(str.substring(beg));
//                    } else {
//                        // The following is OK, because String.substring( beg, end ) excludes
//                        // the character at the position 'end'.
//                        substrings.add(str.substring(beg, end));
//                        // Set the starting point for the next search.
//                        // The following is equivalent to beg = end + (separatorLength - 1) + 1,
//                        // which is the right calculation:
//                        beg = end + separatorLength;
//                    }
//                } else {
//                    // We found a consecutive occurrence of the separator, so skip it.
//                    if (preserveAllTokens) {
//                        numberOfSubstrings += 1;
//                        if (numberOfSubstrings == max) {
//                            end = len;
//                            substrings.add(str.substring(beg));
//                        } else {
//                            substrings.add(EMPTY);
//                        }
//                    }
//                    beg = end + separatorLength;
//                }
//            } else {
//                // String.substring( beg ) goes from 'beg' to the end of the String.
//                substrings.add(str.substring(beg));
//                end = len;
//            }
//        }
//        return substrings.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
//    }
//
//    public static String[] splitPreserveAllTokens(final String str) {
//        return splitWorker(str, null, -1, true);
//    }
//
//    public static String[] splitPreserveAllTokens(final String str, final char separatorChar) {
//        return splitWorker(str, separatorChar, true);
//    }
//
//    public static String[] splitPreserveAllTokens(final String str, final String separatorChars) {
//        return splitWorker(str, separatorChars, -1, true);
//    }
//
//    public static String[] splitPreserveAllTokens(final String str, final String separatorChars, final int max) {
//        return splitWorker(str, separatorChars, max, true);
//    }
//
//
//    private static String[] splitWorker(final String str, final char separatorChar, final boolean preserveAllTokens) {
//        // Performance tuned for 2.0 (JDK1.4)
//        if (str == null) {
//            return null;
//        }
//        final int len = str.length();
//        if (len == 0) {
//            return ArrayUtils.EMPTY_STRING_ARRAY;
//        }
//        final List<String> list = new ArrayList<>();
//        int i = 0;
//        int start = 0;
//        boolean match = false;
//        boolean lastMatch = false;
//        while (i < len) {
//            if (str.charAt(i) == separatorChar) {
//                if (match || preserveAllTokens) {
//                    list.add(str.substring(start, i));
//                    match = false;
//                    lastMatch = true;
//                }
//                start = ++i;
//                continue;
//            }
//            lastMatch = false;
//            match = true;
//            i++;
//        }
//        if (match || preserveAllTokens && lastMatch) {
//            list.add(str.substring(start, i));
//        }
//        return list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
//    }
//
//    private static String[] splitWorker(final String str, final String separatorChars, final int max, final boolean preserveAllTokens) {
//        // Performance tuned for 2.0 (JDK1.4)
//        // Direct code is quicker than StringTokenizer.
//        // Also, StringTokenizer uses isSpace() not isWhitespace()
//        if (str == null) {
//            return null;
//        }
//        final int len = str.length();
//        if (len == 0) {
//            return ArrayUtils.EMPTY_STRING_ARRAY;
//        }
//        final List<String> list = new ArrayList<>();
//        int sizePlus1 = 1;
//        int i = 0;
//        int start = 0;
//        boolean match = false;
//        boolean lastMatch = false;
//        if (separatorChars == null) {
//            // Null separator means use whitespace
//            while (i < len) {
//                if (Character.isWhitespace(str.charAt(i))) {
//                    if (match || preserveAllTokens) {
//                        lastMatch = true;
//                        if (sizePlus1++ == max) {
//                            i = len;
//                            lastMatch = false;
//                        }
//                        list.add(str.substring(start, i));
//                        match = false;
//                    }
//                    start = ++i;
//                    continue;
//                }
//                lastMatch = false;
//                match = true;
//                i++;
//            }
//        } else if (separatorChars.length() == 1) {
//            // Optimize 1 character case
//            final char sep = separatorChars.charAt(0);
//            while (i < len) {
//                if (str.charAt(i) == sep) {
//                    if (match || preserveAllTokens) {
//                        lastMatch = true;
//                        if (sizePlus1++ == max) {
//                            i = len;
//                            lastMatch = false;
//                        }
//                        list.add(str.substring(start, i));
//                        match = false;
//                    }
//                    start = ++i;
//                    continue;
//                }
//                lastMatch = false;
//                match = true;
//                i++;
//            }
//        } else {
//            // standard case
//            while (i < len) {
//                if (separatorChars.indexOf(str.charAt(i)) >= 0) {
//                    if (match || preserveAllTokens) {
//                        lastMatch = true;
//                        if (sizePlus1++ == max) {
//                            i = len;
//                            lastMatch = false;
//                        }
//                        list.add(str.substring(start, i));
//                        match = false;
//                    }
//                    start = ++i;
//                    continue;
//                }
//                lastMatch = false;
//                match = true;
//                i++;
//            }
//        }
//        if (match || preserveAllTokens && lastMatch) {
//            list.add(str.substring(start, i));
//        }
//        return list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
//    }
//
//    public static String strip(final String str) {
//        return strip(str, null);
//    }
//
//    public static String strip(String str, final String stripChars) {
//        str = stripStart(str, stripChars);
//        return stripEnd(str, stripChars);
//    }
//
//    public static String stripAccents(final String input) {
//        if (isEmpty(input)) {
//            return input;
//        }
//        final StringBuilder decomposed = new StringBuilder(Normalizer.normalize(input, Normalizer.Form.NFKD));
//        convertRemainingAccentCharacters(decomposed);
//        return STRIP_ACCENTS_PATTERN.matcher(decomposed).replaceAll(EMPTY);
//    }
//
//    public static String[] stripAll(final String... strs) {
//        return stripAll(strs, null);
//    }
//
//
//    public static String[] stripAll(final String[] strs, final String stripChars) {
//        final int strsLen = ArrayUtils.getLength(strs);
//        if (strsLen == 0) {
//            return strs;
//        }
//        return ArrayUtils.setAll(new String[strsLen], i -> strip(strs[i], stripChars));
//    }
//
//    public static String stripEnd(final String str, final String stripChars) {
//        int end = length(str);
//        if (end == 0) {
//            return str;
//        }
//        if (stripChars == null) {
//            while (end != 0 && Character.isWhitespace(str.charAt(end - 1))) {
//                end--;
//            }
//        } else if (stripChars.isEmpty()) {
//            return str;
//        } else {
//            while (end != 0 && stripChars.indexOf(str.charAt(end - 1)) != INDEX_NOT_FOUND) {
//                end--;
//            }
//        }
//        return str.substring(0, end);
//    }
//
//    public static String stripStart(final String str, final String stripChars) {
//        final int strLen = length(str);
//        if (strLen == 0) {
//            return str;
//        }
//        int start = 0;
//        if (stripChars == null) {
//            while (start != strLen && Character.isWhitespace(str.charAt(start))) {
//                start++;
//            }
//        } else if (stripChars.isEmpty()) {
//            return str;
//        } else {
//            while (start != strLen && stripChars.indexOf(str.charAt(start)) != INDEX_NOT_FOUND) {
//                start++;
//            }
//        }
//        return str.substring(start);
//    }
//
//    public static String stripToEmpty(final String str) {
//        return str == null ? EMPTY : strip(str, null);
//    }
//
//    public static String stripToNull(String str) {
//        if (str == null) {
//            return null;
//        }
//        str = strip(str, null);
//        return str.isEmpty() ? null : str; // NOSONARLINT str cannot be null here
//    }
//
//    public static String substring(final String str, int start) {
//        if (str == null) {
//            return null;
//        }
//        // handle negatives, which means last n characters
//        if (start < 0) {
//            start = str.length() + start; // remember start is negative
//        }
//        if (start < 0) {
//            start = 0;
//        }
//        if (start > str.length()) {
//            return EMPTY;
//        }
//        return str.substring(start);
//    }
//
//
//    public static String substring(final String str, int start, int end) {
//        if (str == null) {
//            return null;
//        }
//
//        if (end < 0) {
//            end = str.length() + end;
//        }
//        if (start < 0) {
//            start = str.length() + start;
//        }
//
//        if (end > str.length()) {
//            end = str.length();
//        }
//
//        if (start > end) {
//            return EMPTY;
//        }
//        if (start < 0) {
//            start = 0;
//        }
//        if (end < 0) {
//            end = 0;
//        }
//        return str.substring(start, end);
//    }
//
//
//    public static String substringAfter(final String str, final int find) {
//        if (isEmpty(str)) {
//            return str;
//        }
//        final int pos = str.indexOf(find);
//        if (pos == INDEX_NOT_FOUND) {
//            return EMPTY;
//        }
//        return str.substring(pos + 1);
//    }
//
//    public static String substringAfter(final String str, final String find) {
//        if (isEmpty(str)) {
//            return str;
//        }
//        if (find == null) {
//            return EMPTY;
//        }
//        final int pos = str.indexOf(find);
//        if (pos == INDEX_NOT_FOUND) {
//            return EMPTY;
//        }
//        return str.substring(pos + find.length());
//    }
//
//    public static String substringAfterLast(final String str, final int find) {
//        if (isEmpty(str)) {
//            return str;
//        }
//        final int pos = str.lastIndexOf(find);
//        if (pos == INDEX_NOT_FOUND || pos == str.length() - 1) {
//            return EMPTY;
//        }
//        return str.substring(pos + 1);
//    }
//
//    public static String substringAfterLast(final String str, final String find) {
//        if (isEmpty(str)) {
//            return str;
//        }
//        if (isEmpty(find)) {
//            return EMPTY;
//        }
//        final int pos = str.lastIndexOf(find);
//        if (pos == INDEX_NOT_FOUND || pos == str.length() - find.length()) {
//            return EMPTY;
//        }
//        return str.substring(pos + find.length());
//    }
//
//    public static String substringBefore(final String str, final int find) {
//        if (isEmpty(str)) {
//            return str;
//        }
//        final int pos = str.indexOf(find);
//        if (pos == INDEX_NOT_FOUND) {
//            return str;
//        }
//        return str.substring(0, pos);
//    }
//
//    public static String substringBefore(final String str, final String find) {
//        if (isEmpty(str) || find == null) {
//            return str;
//        }
//        if (find.isEmpty()) {
//            return EMPTY;
//        }
//        final int pos = str.indexOf(find);
//        if (pos == INDEX_NOT_FOUND) {
//            return str;
//        }
//        return str.substring(0, pos);
//    }
//
//    public static String substringBeforeLast(final String str, final String find) {
//        if (isEmpty(str) || isEmpty(find)) {
//            return str;
//        }
//        final int pos = str.lastIndexOf(find);
//        if (pos == INDEX_NOT_FOUND) {
//            return str;
//        }
//        return str.substring(0, pos);
//    }
//
//    public static String substringBetween(final String str, final String tag) {
//        return substringBetween(str, tag, tag);
//    }
//
//    public static String substringBetween(final String str, final String open, final String close) {
//        if (!ObjectUtils.allNotNull(str, open, close)) {
//            return null;
//        }
//        final int start = str.indexOf(open);
//        if (start != INDEX_NOT_FOUND) {
//            final int end = str.indexOf(close, start + open.length());
//            if (end != INDEX_NOT_FOUND) {
//                return str.substring(start + open.length(), end);
//            }
//        }
//        return null;
//    }
//
//    public static String[] substringsBetween(final String str, final String open, final String close) {
//        if (str == null || isEmpty(open) || isEmpty(close)) {
//            return null;
//        }
//        final int strLen = str.length();
//        if (strLen == 0) {
//            return ArrayUtils.EMPTY_STRING_ARRAY;
//        }
//        final int closeLen = close.length();
//        final int openLen = open.length();
//        final List<String> list = new ArrayList<>();
//        int pos = 0;
//        while (pos < strLen - closeLen) {
//            int start = str.indexOf(open, pos);
//            if (start < 0) {
//                break;
//            }
//            start += openLen;
//            final int end = str.indexOf(close, start);
//            if (end < 0) {
//                break;
//            }
//            list.add(str.substring(start, end));
//            pos = end + closeLen;
//        }
//        if (list.isEmpty()) {
//            return null;
//        }
//        return list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
//    }
//
//    public static String swapCase(final String str) {
//        if (isEmpty(str)) {
//            return str;
//        }
//        final int strLen = str.length();
//        final int[] newCodePoints = new int[strLen]; // cannot be longer than the char array
//        int outOffset = 0;
//        for (int i = 0; i < strLen;) {
//            final int oldCodepoint = str.codePointAt(i);
//            final int newCodePoint;
//            if (Character.isUpperCase(oldCodepoint) || Character.isTitleCase(oldCodepoint)) {
//                newCodePoint = Character.toLowerCase(oldCodepoint);
//            } else if (Character.isLowerCase(oldCodepoint)) {
//                newCodePoint = Character.toUpperCase(oldCodepoint);
//            } else {
//                newCodePoint = oldCodepoint;
//            }
//            newCodePoints[outOffset++] = newCodePoint;
//            i += Character.charCount(newCodePoint);
//        }
//        return new String(newCodePoints, 0, outOffset);
//    }
//
//    public static int[] toCodePoints(final CharSequence cs) {
//        if (cs == null) {
//            return null;
//        }
//        if (cs.length() == 0) {
//            return ArrayUtils.EMPTY_INT_ARRAY;
//        }
//        return cs.toString().codePoints().toArray();
//    }
//
//    public static String toEncodedString(final byte[] bytes, final Charset charset) {
//        return new String(bytes, Charsets.toCharset(charset));
//    }
//
//    public static String toRootLowerCase(final String source) {
//        return source == null ? null : source.toLowerCase(Locale.ROOT);
//    }
//
//    public static String toRootUpperCase(final String source) {
//        return source == null ? null : source.toUpperCase(Locale.ROOT);
//    }
//
//    public static String trim(final String str) {
//        return str == null ? null : str.trim();
//    }
//
//
//    public static String trimToEmpty(final String str) {
//        return str == null ? EMPTY : str.trim();
//    }
//
//
//    public static String trimToNull(final String str) {
//        final String ts = trim(str);
//        return isEmpty(ts) ? null : ts;
//    }
//
//    public static String truncate(final String str, final int maxWidth) {
//        return truncate(str, 0, maxWidth);
//    }
//
//
//    public static String truncate(final String str, final int offset, final int maxWidth) {
//        if (offset < 0) {
//            throw new IllegalArgumentException("offset cannot be negative");
//        }
//        if (maxWidth < 0) {
//            throw new IllegalArgumentException("maxWith cannot be negative");
//        }
//        if (str == null) {
//            return null;
//        }
//        if (offset > str.length()) {
//            return EMPTY;
//        }
//        if (str.length() > maxWidth) {
//            final int ix = Math.min(offset + maxWidth, str.length());
//            return str.substring(offset, ix);
//        }
//        return str.substring(offset);
//    }
//
//    public static String uncapitalize(final String str) {
//        final int strLen = length(str);
//        if (strLen == 0) {
//            return str;
//        }
//        final int firstCodePoint = str.codePointAt(0);
//        final int newCodePoint = Character.toLowerCase(firstCodePoint);
//        if (firstCodePoint == newCodePoint) {
//            // already uncapitalized
//            return str;
//        }
//        final int[] newCodePoints = str.codePoints().toArray();
//        newCodePoints[0] = newCodePoint; // copy the first code point
//        return new String(newCodePoints, 0, newCodePoints.length);
//    }
//
//    public static String unwrap(final String str, final char wrapChar) {
//        if (isEmpty(str) || wrapChar == CharUtils.NUL || str.length() == 1) {
//            return str;
//        }
//        if (str.charAt(0) == wrapChar && str.charAt(str.length() - 1) == wrapChar) {
//            final int startIndex = 0;
//            final int endIndex = str.length() - 1;
//            return str.substring(startIndex + 1, endIndex);
//        }
//        return str;
//    }
//
//    public static String unwrap(final String str, final String wrapToken) {
//        if (isEmpty(str) || isEmpty(wrapToken) || str.length() < 2 * wrapToken.length()) {
//            return str;
//        }
//        if (Strings.CS.startsWith(str, wrapToken) && Strings.CS.endsWith(str, wrapToken)) {
//            return str.substring(wrapToken.length(), str.lastIndexOf(wrapToken));
//        }
//        return str;
//    }
//
//    public static String upperCase(final String str) {
//        if (str == null) {
//            return null;
//        }
//        return str.toUpperCase();
//    }
//
//    public static String upperCase(final String str, final Locale locale) {
//        if (str == null) {
//            return null;
//        }
//        return str.toUpperCase(LocaleUtils.toLocale(locale));
//    }
//
//    public static String valueOf(final char[] value) {
//        return value == null ? null : String.valueOf(value);
//    }
//
//    public static String wrap(final String str, final char wrapWith) {
//        if (isEmpty(str) || wrapWith == CharUtils.NUL) {
//            return str;
//        }
//        return wrapWith + str + wrapWith;
//    }
//
//    public static String wrap(final String str, final String wrapWith) {
//        if (isEmpty(str) || isEmpty(wrapWith)) {
//            return str;
//        }
//        return wrapWith.concat(str).concat(wrapWith);
//    }
//
//    public static String wrapIfMissing(final String str, final char wrapWith) {
//        if (isEmpty(str) || wrapWith == CharUtils.NUL) {
//            return str;
//        }
//        final boolean wrapStart = str.charAt(0) != wrapWith;
//        final boolean wrapEnd = str.charAt(str.length() - 1) != wrapWith;
//        if (!wrapStart && !wrapEnd) {
//            return str;
//        }
//        final StringBuilder builder = new StringBuilder(str.length() + 2);
//        if (wrapStart) {
//            builder.append(wrapWith);
//        }
//        builder.append(str);
//        if (wrapEnd) {
//            builder.append(wrapWith);
//        }
//        return builder.toString();
//    }
//
//    public static String wrapIfMissing(final String str, final String wrapWith) {
//        if (isEmpty(str) || isEmpty(wrapWith)) {
//            return str;
//        }
//        final boolean wrapStart = !str.startsWith(wrapWith);
//        final boolean wrapEnd = !str.endsWith(wrapWith);
//        if (!wrapStart && !wrapEnd) {
//            return str;
//        }
//        final StringBuilder builder = new StringBuilder(str.length() + wrapWith.length() + wrapWith.length());
//        if (wrapStart) {
//            builder.append(wrapWith);
//        }
//        builder.append(str);
//        if (wrapEnd) {
//            builder.append(wrapWith);
//        }
//        return builder.toString();
//    }
//
//    /**
//     * {@link StringUtils} instances should NOT be constructed in standard programming. Instead, the class should be used as {@code StringUtils.trim(" foo ");}.
//     *
//     * <p>
//     * This constructor is public to permit tools that require a JavaBean instance to operate.
//     * </p>
//     *
//     * @deprecated TODO Make private in 4.0.
//     */
//    @Deprecated
//    public StringUtils() {
//        // empty
//    }
//
//
//}
